namespace ScrumSprintMonitor.Design
{
	#region #using Directives

	using System;
	using System.Collections.Generic;
	using System.ComponentModel;
	using System.Linq;
	using System.Windows.Media;

	using Implementation;
	using Implementation.Configuration;

	using Infrastructure;
	using Infrastructure.Configuration;

	using Microsoft.Practices.ServiceLocation;

	using Rhino.Mocks;

	#endregion

	public sealed class TestAssetLibrary
	{
		public static ISprintConfiguration CreateSprintConfiguration()
		{
			var sprintConfiguration = MockRepository.GenerateStub<ISprintConfiguration>();

			sprintConfiguration.ProjectName = "Test Project";
			sprintConfiguration.ProcessTemplateName = "Conchango";
			sprintConfiguration.IterationPath = @"Release 1\Sprint 6";
			sprintConfiguration.BuildDefinitionNames = new[] {"Test CI Build 1", "Test CI Build 2"};
			sprintConfiguration.Stub(c => c.IsReadOnly).Return(true);
			sprintConfiguration.Stub(c => c.Save()).Throw(new NotImplementedException());
			sprintConfiguration.SprintStartTimeZone = TimeZoneInfo.Utc;
			sprintConfiguration.SprintStartDate = new DateTime(2009, 1, 21, 0, 0, 0);
			sprintConfiguration.SprintEndDate = new DateTime(2009, 2, 10, 0, 0, 0);
			sprintConfiguration.SprintEndTimeZone = TimeZoneInfo.Utc;

			return sprintConfiguration;
		}

		public static ITeamMemberInfoProvider CreateTeamMemberInfoProvider()
		{
			var unassigned = new UsTeamMemberInformation
			                 	{
			                 		Name = null
			                 	};
			var matt = new UsTeamMemberInformation
			           	{
			           		Name = "Matt"
			           	};
			var roger = new UsTeamMemberInformation
			            	{
			            		Name = "Roger",
			            		DedicatedToSprint = false
			            	};
			var robert = new UsTeamMemberInformation
			             	{
			             		Name = "Robert"
			             	};
			var surya = new UsTeamMemberInformation
			            	{
			            		Name = "Surya"
			            	};
			var jesse = new UsTeamMemberInformation
			            	{
			            		Name = "Jesse"
			            	};
			var rolando = new UsTeamMemberInformation
			              	{
			              		Name = "Rolando"
			              	};
			var rohanna = new UsTeamMemberInformation
			              	{
			              		Name = "Rohanna"
			              	};
			var pedro = new PtTeamMemberInformation
			            	{
			            		Name = "Pedro"
			            	};
			var samuel = new PtTeamMemberInformation
			             	{
			             		Name = "Samuel"
			             	};
			var teamInformation = new List<ITeamMemberInformation>
			                      	{
			                      		matt,
			                      		roger,
			                      		robert,
			                      		surya,
			                      		jesse,
			                      		rolando,
			                      		rohanna,
			                      		pedro,
			                      		samuel,
			                      	};
			return new TeamInformationProvider(teamInformation, unassigned);
		}

		public static IConnection CreateTfsConnectionStub()
		{
			return CreateTfsConnectionStub(CreateSprintConfiguration());
		}

		public static IConnection CreateTfsConnectionStub(ISprintConfiguration sprintConfiguration)
		{
			var connection = MockRepository.GenerateStub<IConnection>();
			var buildInfo = MockRepository.GenerateStub<IBuildInfo>();

			buildInfo.Stub(b => b.Status).Return(BuildStatus.Succeeded);
			buildInfo.Stub(b => b.Number).Return("Project CI Build 2009.2.6.3");
			buildInfo.Stub(b => b.FinishTime).Return(SystemTime.Now().Date.AddHours(9));
			connection.Stub(c => c.GetBuildInfo(sprintConfiguration)).Return(buildInfo);
			connection.Stub(c => c.GetSprintName(sprintConfiguration)).Return("Sprint 4");
			connection.Stub(c => c.IsConnected).Return(true);

			return connection;
		}

		public static BindingList<IWorkItemAdapter> CreateWorkItems()
		{
			int id = 1;

			var list = new BindingList<IWorkItemAdapter>
			           	{
			           		CreateWorkItem(id++, "Pedro", WorkItemState.NotDone, 0.0, 4.0),
			           		CreateWorkItem(id++, "Pedro", WorkItemState.NotDone, 0.0, 16.0),
			           		CreateWorkItem(id++, "Pedro", WorkItemState.InProgress, 1.0, 3.0),
			           		CreateWorkItem(id++, "Pedro", WorkItemState.Done, 6.0, 0.0),
			           		CreateWorkItem(id++, "Roger", WorkItemState.NotDone, 0.0, 16.0),
			           	};

			return list;
		}

		public static BindingList<IWorkItemAdapter> CreateWorkItemsWithWorkNotEstimated()
		{
			var list = CreateWorkItems();

			list.Add(CreateWorkItemWithNoRemainingWorkEstimate(10, "Roger"));

			return list;
		}

		public static IWorkItemAdapter CreateWorkItem(int id, string assignedTo, WorkItemState state, double doneWork, double remainingWork)
		{
			return CreateWorkItem(id, WorkItemType.SprintBacklogItem, assignedTo, state, doneWork, remainingWork);
		}

		public static IWorkItemAdapter CreateWorkItem(int id, string assignedTo, double remainingWork)
		{
			return CreateWorkItem(id, WorkItemType.SprintBacklogItem, assignedTo, WorkItemState.NotDone, 0.0, remainingWork);
		}

		public static IWorkItemAdapter CreateWorkItem(int id, WorkItemType type, string assignedTo, WorkItemState state, double doneWork,
		                                              double remainingWork)
		{
			var workItemAdapter = MockRepository.GenerateStub<IWorkItemAdapter>();

			workItemAdapter.Stub(wi => wi.Id).Return(id);
			workItemAdapter.Stub(wi => wi.Type).Return(type);
			workItemAdapter.Stub(wi => wi.AssignedTo).Return(assignedTo);
			workItemAdapter.Stub(wi => wi.DoneWork).Return(doneWork);
			workItemAdapter.Stub(wi => wi.RemainingWork).Return(remainingWork);
			workItemAdapter.Stub(wi => wi.State).Return(state);

			return workItemAdapter;
		}

		private static IWorkItemAdapter CreateWorkItemWithNoRemainingWorkEstimate(int id, string assignedTo)
		{
			var workItemAdapter = MockRepository.GenerateStub<IWorkItemAdapter>();

			workItemAdapter.Stub(wi => wi.Id).Return(id);
			workItemAdapter.Stub(wi => wi.Type).Return(WorkItemType.SprintBacklogItem);
			workItemAdapter.Stub(wi => wi.AssignedTo).Return(assignedTo);
			workItemAdapter.Stub(wi => wi.DoneWork).Return(0.0);
			workItemAdapter.Stub(wi => wi.RemainingWork).Return(double.NaN);
			workItemAdapter.Stub(wi => wi.State).Return(WorkItemState.NotDone);

			return workItemAdapter;
		}

		public static IReportableData CreateReportableData()
		{
			return MockRepository.GenerateStub<IReportableData>();
		}

		public static IWorkItemStore CreateWorkItemStore(BindingList<IWorkItemAdapter> workItemAdapters)
		{
			var workItemStore = MockRepository.GenerateStub<IWorkItemStore>();

			workItemStore.Stub(s => s.WorkItems).Return(workItemAdapters);

			var refreshedEventRaiser = workItemStore.GetEventRaiser(x => x.WorkItemsRefreshed += null);
			var updatedEventRaiser = workItemStore.GetEventRaiser(x => x.WorkItemsUpdated += null);
			workItemStore.Stub(s => s.Refresh()).IgnoreArguments().Do((Action) (() =>
			                                                                    {
			                                                                    	refreshedEventRaiser.Raise(workItemStore, new RefreshResultsEventArgs(null));
			                                                                    	updatedEventRaiser.Raise(workItemStore, EventArgs.Empty);
			                                                                    }));

			return workItemStore;
		}

		public static void ConfigureSprintWithMockData(ISprint sprint, IServiceLocator serviceLocator)
		{
			var sprintConfiguration = serviceLocator.GetInstance<ISprintConfiguration>();
			var connection = CreateTfsConnectionStub(sprintConfiguration);

			sprint.Stub(s => s.AvailableTime).Return(TimeSpan.FromHours(80));
			sprint.Stub(s => s.TotalTime).Return(TimeSpan.FromHours(200));
			sprint.Stub(s => s.TimeIncludesFreeBusyInformation).Return(true);
			sprint.Stub(s => s.Start).Return(sprintConfiguration.SprintEndDate);
			sprint.Stub(s => s.End).Return(sprintConfiguration.SprintEndDate);
			sprint.Stub(s => s.LastRefresh).Return(SystemTime.Now());
			sprint.Stub(s => s.Name).Return(connection.GetSprintName(sprintConfiguration));
			sprint.Stub(s => s.Status).Return(SprintStatus.OK);
			var workItemStore = CreateWorkItemStore(CreateWorkItems());
			sprint.Stub(s => s.WorkItemStore).Return(workItemStore);
		}

		public static IList<ITeamMember> CreateTeamMembers(ITeamMemberInfoProvider teamMemberInfoProvider, IEnumerable<IWorkItemAdapter> workItems)
		{
			var teamMembersList = new BindingList<ITeamMember>
			                      	{
			                      		new TeamMember(teamMemberInfoProvider.GetTeamMemberInformation("Roger"),
			                      		               workItems.Where(
			                      		               	x => x.Type == WorkItemType.SprintBacklogItem && x.AssignedTo == "Roger"),
			                      		               new TeamMemberRemainingWorkload()),
			                      		new TeamMember(teamMemberInfoProvider.GetTeamMemberInformation("Pedro"),
			                      		               workItems.Where(
			                      		               	x => x.Type == WorkItemType.SprintBacklogItem && x.AssignedTo == "Pedro"),
			                      		               new TeamMemberRemainingWorkload())
			                      	};

			return teamMembersList;
		}

		#region Nested type: HardcodedPresentationConfiguration

		public class HardcodedPresentationConfiguration : IPresentationConfiguration
		{
			private readonly RemainingWorkStatusPresentation[] ranges;

			public HardcodedPresentationConfiguration()
			{
				var defaultRanges = new List<RemainingWorkStatusPresentation>();
				AddDefaultRange(RemainingWorkStatus.NoWorkLeft, Colors.Transparent, Colors.White, 0.0, defaultRanges);
				AddDefaultRange(RemainingWorkStatus.SeverelyUnderassigned, Colors.MidnightBlue, Colors.White, 0.5, defaultRanges);
				AddDefaultRange(RemainingWorkStatus.Underassigned, Color.FromRgb(0, 0, 200), Colors.White, 0.8, defaultRanges);
				AddDefaultRange(RemainingWorkStatus.Normal, Color.FromRgb(0, 200, 0), Colors.White, 1.0, defaultRanges);
				AddDefaultRange(RemainingWorkStatus.Warning, Colors.Orange, Colors.White, 1.05, defaultRanges);
				AddDefaultRange(RemainingWorkStatus.Overassigned, Color.FromRgb(225, 0, 0), Colors.White, 1.20, defaultRanges);
				AddDefaultRange(RemainingWorkStatus.SeverelyOverassigned, Color.FromRgb(200, 0, 0), Colors.White, double.PositiveInfinity, defaultRanges);

				this.ranges = defaultRanges.ToArray();
			}

			private static void AddDefaultRange(RemainingWorkStatus status, Color backgroundColor, Color foregroundColor, double upperLimit,
			                                    ICollection<RemainingWorkStatusPresentation> defaultRanges)
			{
				defaultRanges.Add(new RemainingWorkStatusPresentation(status, defaultRanges.LastOrDefault())
				                  	{
				                  		BackgroundColor = backgroundColor,
				                  		ForegroundColor = foregroundColor,
				                  		UpperLimit = upperLimit
				                  	});
			}

			#region Implementation of IPresentationConfiguration

			public IRemainingWorkStatusPresentationConfig GetRemainingWorkStatusPresentationConfig(RemainingWorkStatus status)
			{
				int index = (int) status;
				if (index < 0 || index >= this.ranges.Length)
				{
					throw new ArgumentException("status");
				}

				return this.ranges[index];
			}

			public bool IsReadOnly
			{
				get { return true; }
			}

			public void Save()
			{
				throw new NotImplementedException();
			}

			#endregion
		}

		#endregion

		#region Nested type: HardcodedRemainingWorkStatusEvaluator

		public class HardcodedRemainingWorkStatusEvaluator : IRemainingWorkStatusEvaluator
		{
			#region IRemainingWorkStatusEvaluator Members

			public RemainingWorkStatus Evaluate(double remainingWork, double availableTime)
			{
				double ratio = remainingWork / availableTime;

				if (ratio >= 1.20)
				{
					return RemainingWorkStatus.SeverelyOverassigned;
				}
				if (ratio >= 1.05)
				{
					return RemainingWorkStatus.Overassigned;
				}
				if (ratio > 1.0)
				{
					return RemainingWorkStatus.Warning;
				}
				if (ratio >= 0.8)
				{
					return RemainingWorkStatus.Normal;
				}
				if (ratio >= 0.5)
				{
					return RemainingWorkStatus.Underassigned;
				}
				if (double.IsNaN(ratio))
				{
					return RemainingWorkStatus.NoWorkLeft;
				}

				return RemainingWorkStatus.SeverelyUnderassigned;
			}

			#endregion
		}

		#endregion

		#region Nested type: PtTeamMemberInformation

		private class PtTeamMemberInformation : ITeamMemberInformation
		{
			public PtTeamMemberInformation()
			{
				DedicatedToSprint = true;
			}

			#region Implementation of ITeamMemberInformation

			public string Name { get; internal set; }

			public string DisplayAlias { get; set; }

			public IAvatar Avatar
			{
				get { return null; }
				set { throw new NotImplementedException(); }
			}

			public string SchedulerAlias
			{
				get { return null; }
			}

			public TimeZoneInfo TimeZone
			{
				get { return TimeZoneInfo.Utc; }
			}

			public double HourlyProductivityFactor
			{
				get { return 1.0; }
			}

			public bool DedicatedToSprint { get; set; }

			public DateTime GetWorkdayStart(DateTime date)
			{
				var startTime = date.Date.AddHours(9);

				return startTime;
			}

			public DateTime GetWorkdayEnd(DateTime date)
			{
				var endTime = date.Date.AddHours(18);

				return endTime;
			}

			public bool IsBusy(DateTime time, TimeSpan slotSize)
			{
				return (time.Hour >= 12 && time.Hour < 13);
			}

			#endregion

			#region Implementation of INotifyPropertyChanged

			public event PropertyChangedEventHandler PropertyChanged;

			#endregion
		}

		#endregion

		#region Nested type: TeamInformationProvider

		public class TeamInformationProvider : ITeamMemberInfoProvider
		{
			private readonly List<ITeamMemberInformation> list;
			private readonly ITeamMemberInformation unassignedTeamMemberInfo;

			public TeamInformationProvider(List<ITeamMemberInformation> list, ITeamMemberInformation unassignedTeamMemberInfo)
			{
				this.list = list;
				this.unassignedTeamMemberInfo = unassignedTeamMemberInfo;
			}

			#region Implementation of ITeamMemberInfoProvider

			public ITeamMemberInformation GetTeamMemberInformation(string teamMemberName)
			{
				if (string.IsNullOrEmpty(teamMemberName))
				{
					return this.unassignedTeamMemberInfo;
				}

				return this.list.Find(x => x.Name == teamMemberName);
			}

			public IEnumerable<ITeamMemberInformation> TeamMembers
			{
				get { return this.list; }
			}

			#endregion

			#region INotifyPropertyChanged members

			public event PropertyChangedEventHandler PropertyChanged;

			public void OnPropertyChanged(string propertyName)
			{
				if (PropertyChanged != null)
				{
					PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
				}
			}

			#endregion
		}

		#endregion

		#region Nested type: UsTeamMemberInformation

		public class UsTeamMemberInformation : ITeamMemberInformation
		{
			public UsTeamMemberInformation()
			{
				DedicatedToSprint = true;
			}

			#region Implementation of ITeamMemberInformation

			public string Name { get; set; }

			public string DisplayAlias { get; set; }
			public IAvatar Avatar { get; set; }

			public string SchedulerAlias
			{
				get { return null; }
			}

			public TimeZoneInfo TimeZone
			{
				get { return TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time"); }
			}

			public double HourlyProductivityFactor
			{
				get { return 1.0; }
			}

			public bool DedicatedToSprint { get; set; }

			public DateTime GetWorkdayStart(DateTime date)
			{
				var startTime = date.Date.AddHours(8);

				return startTime;
			}

			public DateTime GetWorkdayEnd(DateTime date)
			{
				var endTime = date.Date.AddHours(17);

				return endTime;
			}

			public bool IsBusy(DateTime time, TimeSpan slotSize)
			{
				return (time.Hour >= 12 && time.Hour < 13);
			}

			#endregion

			#region Implementation of INotifyPropertyChanged

			public event PropertyChangedEventHandler PropertyChanged;

			#endregion
		}

		#endregion
	}
}